package com.froggushka.api.onehourtranslation;

/*
Copyright (c) 2011 Froggushka

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

//https://github.com/OneHourTranslation/PHP-Client-Library/blob/master/lib/OHTAPI.php
//http://www.onehourtranslation.com/resources/remote-api/new-proofreading-project/
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.utils.URIUtils;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;

public class OneHourTranslationService
{
  private final Log log = LogFactory.getLog(getClass());

  public enum ConnectionTypeEnum
  {
    TESTING, PRODUCTION
  };

  public enum MethodTypeEnum
  {
    GET, POST
  };

  private ConnectionTypeEnum m_eConnectionType;
  private String m_strAccountID;
  private String m_strSecretKey;

  private static final String OHT_PRODUCTION_HOST = "api.onehourtranslation.com";
  private static final String OHT_TESTING_HOST = "sandbox.onehourtranslation.com";

  private static final String SCHEME = "https";
  private static final String OHT_API_VERSION = "/api/1";

  private static final String NEW_PROJECT_PATH = OHT_API_VERSION + "/project/new/";
  private static final String PROOFREADING_PROJECT_PATH = OHT_API_VERSION + "/project/new/";
  private static final String ACCOUNT_DETAILS_PATH = OHT_API_VERSION + "/account/details/";
  private static final String PROJECT_DETAILS_PATH = OHT_API_VERSION + "/project/%1$s/details/";
  private static final String PROJECT_TRANSLATION_PATH = OHT_API_VERSION + "/project/%1$s/contents/";

  public OneHourTranslationService(ConnectionTypeEnum p_oConnectionType, String p_strAccountID, String p_strSecretKey)
  {
    m_eConnectionType = p_oConnectionType;
    m_strAccountID = p_strAccountID;
    m_strSecretKey = p_strSecretKey;
  }

  public String getAccountID()
  {
    return m_strAccountID;
  }

  public String getSecretKey()
  {
    return m_strSecretKey;
  }

  private String request(MethodTypeEnum p_eMethodType, String p_strPath, List<NameValuePair> p_listOfQueryParams) throws OneHourTranslationException
  {
    InputStream instream = null;
    try
    {
      p_listOfQueryParams.add(new BasicNameValuePair("account_id", m_strAccountID));
      p_listOfQueryParams.add(new BasicNameValuePair("secret_key", m_strSecretKey));

      HttpClient httpclient = new DefaultHttpClient();
      HttpUriRequest httpRequest;

      URI uri;

      if (p_eMethodType == MethodTypeEnum.GET)
      {
        uri = URIUtils.createURI(OneHourTranslationService.SCHEME, getHost(), -1, p_strPath, URLEncodedUtils.format(p_listOfQueryParams, "UTF-8"), null);
        httpRequest = new HttpGet(uri);
      }
      else
      {
        uri = URIUtils.createURI(OneHourTranslationService.SCHEME, getHost(), -1, p_strPath, null, null);
        HttpPost httpost = new HttpPost(uri);
        UrlEncodedFormEntity formentity = new UrlEncodedFormEntity(p_listOfQueryParams, "UTF-8");
        httpost.setEntity(formentity);
        httpRequest = httpost;
      }

      HttpResponse response = httpclient.execute(httpRequest);

      if (log.isDebugEnabled())
      {
        StringBuilder traceInfo = new StringBuilder();
        traceInfo.append("URI :").append(httpRequest.getURI());
        traceInfo.append("\n Status :").append(response.getStatusLine().toString());
        log.debug(traceInfo);
      }

      HttpEntity entity = response.getEntity();
      if (entity != null)
      {
        instream = entity.getContent();
        int contentLength = (int) entity.getContentLength();
        int read = 0;
        int offset = 0;
        byte[] buffer = new byte[contentLength];
        do
        {
          read = instream.read(buffer, offset, 4096);
          offset += read;
        }
        while (read != -1 && offset < contentLength);
        return new String(buffer);
      }
    }
    catch (ClientProtocolException exp)
    {
      log.error(exp.getLocalizedMessage(), exp);
      throw new OneHourTranslationException(exp.getLocalizedMessage(), (Exception) exp);
    }
    catch (IOException exp)
    {
      log.error(exp.getLocalizedMessage(), exp);
      throw new OneHourTranslationException(exp.getLocalizedMessage(), (Exception) exp);
    }
    catch (URISyntaxException exp)
    {
      log.error(exp.getLocalizedMessage(), exp);
      throw new OneHourTranslationException(exp.getLocalizedMessage(), (Exception) exp);
    }
    finally
    {
      closeStream(instream);
    }
    return null;
  }

  private String getHost()
  {
    String strHost = null;

    if (m_eConnectionType == ConnectionTypeEnum.PRODUCTION)
    {
      strHost = OHT_PRODUCTION_HOST;
    }
    else
    {
      strHost = OHT_TESTING_HOST;
    }
    return strHost;
  }

  private void closeStream(Closeable p_oStream)
  {
    try
    {
      if (p_oStream != null)
      {
        p_oStream.close();
      }
    }
    catch (IOException exp)
    {
      log.error(exp.getLocalizedMessage(), exp);
    }
  }
  
  /**
   * Initialize a new Translation project on OneHourTranslation.
   *  <br/>
   *    <ul>Mandatory Request Fields
   *    <li>source  Source language see supported languages</li>
   *    <li>target  Target language see supported languages</li>
   *    <li>source_text (string) The source text to be translated</li>
   *    </ul>
   *  
   *  <ul>Response  Values:
   *    <li>status_code Numerical indication of your request status</li> 
   *    <li>status_msg  String indication of your request status</li>  
   *    <li>project_id  (integer) Project ID for reference</li>
   *    <li>word_count  (integer) Reported or calculated word count</li> 
   *    <li>credits (integer) Credits charged</li></ul>
   *    <ul>OR 
   *    <li>status_code Numerical indication of your request status</li> 
   *    <li>status_msg  String indication of your request status</li>
   *    <li>errors  JSON formatted representation of various errors Applicable only on fields-validation errors</li></ul>
   * @param p_oRequestOptions    Request properties
   * @return JSON encoded string. exp {"project_id":"18974","word_count":71,"credits":497,"status_code":0,"status_msg":"ok"}
   * @throws OneHourTranslationException
   */
  public String submitNewProject(RequestOptions p_oRequestOptions) throws OneHourTranslationException
  {
    if (p_oRequestOptions == null)
    {
      return null;
    }

    return request(MethodTypeEnum.POST, NEW_PROJECT_PATH, p_oRequestOptions.getParameters());
  }
  
  /**
   * Initialize a new proof reading project on OneHourTranslation.
   *  <br/>
   *    <ul>Mandatory Request Fields
   *    <li>source  Source language see supported languages</li>
   *    <li>target  Target language see supported languages</li>
   *    <li>translated_text (string) The text to be proof read</li>
   *    </ul>
   *  
   *  <ul>Response  Values:
   *    <li>status_code Numerical indication of your request status</li> 
   *    <li>status_msg  String indication of your request status</li>  
   *    <li>project_id  (integer) Project ID for reference</li>
   *    <li>word_count  (integer) Reported or calculated word count</li> 
   *    <li>credits (integer) Credits charged</li></ul>
   *    <ul>OR 
   *    <li>status_code Numerical indication of your request status</li> 
   *    <li>status_msg  String indication of your request status</li>
   *    <li>errors  JSON formatted representation of various errors Applicable only on fields-validation errors</li></ul>
   * @param p_oRequestOptions    Request properties
   * @return JSON encoded string. exp {"project_id":"18974","word_count":71,"credits":497,"status_code":0,"status_msg":"ok"}
   * @throws OneHourTranslationException
   */
  
  public String submitNewProofreadingProject(RequestOptions p_oRequestOptions) throws OneHourTranslationException
  {
    if (p_oRequestOptions == null)
    {
      return null;
    }
    p_oRequestOptions.setProjectType("proofreading");
    return request(MethodTypeEnum.POST, PROOFREADING_PROJECT_PATH, p_oRequestOptions.getParameters());
  }

  /**
   *Fetch the project's details.
   * <ul>Response  Values:
   *   <li>status_code Numerical indication of your request status</li>  
   *   <li>status_msg  String indication of your request status</li>   
   *   <li>project_id  (integer) Project ID for reference</li>   
   *   <li>word_count  (integer) Reported or calculated word count</li>  
   *   <li>source  (string) Source language</li>   
   *   <li>target  (string) Target language</li>  
   *   <li>credits (integer) Credits reduced from your balance</li>  
   *   <li>estimate_finish (integer) Unix representation of estimated date and time for translation to be ready  GMT</li> 
   *   <li>Available only when project status is "inProcess" or "disputed" and translation was not submitted (see status codes)</li> 
   *   <li>translation_ready (boolean) 1 for translation has been submitted, otherwise 0.</li>   
   *   <li>project_url (string) URL to project page on OneHourTranslation website</li> </ul>
   * @param p_strProjectID    project ID
   * @return JSON encoded string. exp {"project_id":"18942","word_count":"90","source":"english","target":"french","credits":630,"project_status":"translationReceived","estimate_finish":1321725600,"translation_ready":1,"project_url":"https:\/\/www.sandbox.onehourtranslation.com\/project\/18942","status_code":0,"status_msg":"ok"}
   * @throws OneHourTranslationException
   */
  public String getProjectDetails(String p_strProjectID) throws OneHourTranslationException
  {
    RequestOptions oRequestOptions = new RequestOptions();
    String path = String.format(PROJECT_DETAILS_PATH, p_strProjectID);
    return request(MethodTypeEnum.GET, path, oRequestOptions.getParameters());
  }

  /**
   * Fetch the project's translated text and source text.
   * <ul>Response  Values:
   *    <li>status_code Numerical indication of your request status</li>  
   *    <li>status_msg  String indication of your request status</li>   
   *    <li>project_id  (integer) Project ID for reference</li>   
   *    <li>original_content  (string) The original content Base-64 Encoded</li> 
   *    <li>translated_content  (string) The translated content Base-64 Encoded</li> 
   *    <li>content_type  (string) Content format</li> </ul>
   * @param p_strProjectID    project ID
   * @return JSON encoded string. exp {"project_id":"18942","original_content":"QSBib29rIG....","translated_content":"VW4gbGl2cmUgZXN0IH....","content_type":"text\/plain","status_code":0,"status_msg":"ok"}
   * @throws OneHourTranslationException
   */
  public String getProjectTranslation(String p_strProjectID) throws OneHourTranslationException
  {
    RequestOptions oRequestOptions = new RequestOptions();
    String path = String.format(PROJECT_TRANSLATION_PATH, p_strProjectID);
    return request(MethodTypeEnum.GET, path, oRequestOptions.getParameters());
  }

  /**
   * Fetch the user's account details.
   * <ul>Response  Values:
   *    <li>status_code Numerical indication of your request status</li>  
   *    <li>status_msg  String indication of your request status</li>   
   *    <li>account_id  (integer) Account ID for reference</li>   
   *    <li>account_username  (string) Account owner's username</li>  
   *    <li>credit_balance  (integer) Credit balance</li> </ul>
   * @return JSON encoded string. exp {"account_id":"269","account_username":"demo","credit_balance":99938519,"status_code":0,"status_msg":"ok"}
   * @throws OneHourTranslationException
   */
  public String getAccountDetails() throws OneHourTranslationException
  {
    RequestOptions oRequestOptions = new RequestOptions();
    return request(MethodTypeEnum.GET, ACCOUNT_DETAILS_PATH, oRequestOptions.getParameters());
  }
}
